Sử dụng Assertion (phát triển phần mềm)

Trong các ngôn ngữ như Eiffel, assertion là một phần của quá trình thiết kế; còn các ngôn ngữ khác như CJava thì sử dụng chúng chỉ để kiểm tra giả định trong run-time thôi. Trong cả hai trường hợp, assertion đều có thể được dùng để kiểm tra tính hợp lệ trong run-time nhưng cũng thường có thể được ẩn đi.

Assertion trong thiết kế theo khế ước

Assertion có thể mang chức năng như một dạng tài liệu: nó có thể mô tả trạng thái mà code trông mong trước khi chạy (tức điều kiện tiên quyết), và trạng thái mà code mong muốn đưa ra khi chạy xong (tức điều kiện hậu quyết); nó còn có thể đặc tả lượng bất biến[lower-alpha 4] của lớp nữa. Eiffel có tích hợp các assertion như vậy vào ngôn ngữ và tự động trích xuất chúng trở thành tài liệu cho lớp. Đây là một phần quan trọng của phương pháp thiết kế theo khế ước.[lower-alpha 5]

Lối tiếp cận này cũng có hữu ích trong những ngôn ngữ không tường minh hỗ trợ nó: điểm lợi của việc sử dụng câu lệnh assertion chứ không dùng assertion trong comment đó là chương trình có thể kiểm tra assertion mỗi lần nó chạy; nếu assertion không còn thỏa thì có thể báo cáo lỗi. Điều này phòng ngừa chuyện code bị mất đồng bộ với assertion.

Assertion để kiểm tra run-time

Assertion có thể được dùng để xác minh rằng giả định do lập trình viên tạo ra trong quá trình biên soạn chương trình vẫn giữ được tính hợp lệ khi chương trình được thực thi. Ví dụ: xem xét mã Java sau:

 int total = countNumberOfUsers(); if (total % 2 == 0) {   // total là số chẵn } else {   // total là số lẻ và không âm   assert total % 2 == 1; }

Trong Java, % là toán tử lấy số dư (modulo), và trong Java, nếu toán hạng đầu tiên của nó là số âm, thì kết quả cũng có thể là số âm (không giống như modulo được dùng trong toán học). Ở đây, lập trình viên đã giả định rằng total không âm, do đó phần dư của phép chia cho 2 sẽ luôn là 0 hoặc 1. Assertion ở đây làm cho giả định này tường minh: hễ countNumberOfUsers trả về giá trị âm, thì tức là chương trình có thể có bug.[lower-alpha 6]

Một ưu điểm chính yếu của kỹ thuật này đó là khi có lỗi xảy ra, nó sẽ được phát hiện ngay lập tức và một cách trực tiếp, chứ không phải để mãi sau mới phát hiện ra thông qua những tác dụng thường là không rõ ràng của lỗi. Vì sự bất thành của assertion thường báo cáo luôn vị trí code, nên người ta hay có thể định vị được lỗi mà không cần debug gì thêm.

Assertion đôi khi cũng được đặt ở những điểm mà sự thực thi đúng ra không có chuyện tới được. Ví dụ, assertion có thể được đặt ở tiểu cú[lower-alpha 7] default của câu lệnh switch trong các ngôn ngữ như C, C++Java. Bất cứ trường hợp nào mà lập trình viên không chủ ý xử trí thì sẽ giơ lỗi và chương trình sẽ dừng ngang chứ không âm thầm tiếp tục trong trạng thái bị lỗi. Trong D, assertion như vậy được tự động thêm khi câu lệnh switch không chứa tiểu cú default.

Trong Java, assertion là một phần của ngôn ngữ kể từ phiên bản 1.4 đến giờ. Assertion bất thành sẽ kéo theo việc giơ AssertionError khi chương trình được chạy với các cờ thích hợp, nếu không đặt các cờ đấy thì các câu lệnh assertion sẽ được bỏ qua. Trong C, nó được thêm vào thông qua header tiêu chuẩn assert.h trong đó có định nghĩa assert(assertion). Trong C++, cả hai header assert.hcassert đều cung cấp macro assert.

Sự nguy hiểm của assertion đó là nó có thể gây ra tác dụng phụ vì nó có thể thay đổi dữ liệu bộ nhớ hoặc thay đổi việc canh giờ của thread. Assertion nên được thực hiện cẩn thận để chúng không gây ra tác dụng phụ trên mã chương trình.

Kết cấu assertion trong ngôn ngữ cho phép dễ dàng phát triển dẫn động bằng kiểm thử[lower-alpha 8] mà không cần sử dụng thư viện của bên thứ ba.

Assertion trong chu trình phát triển

Trong chu trình phát triển, lập trình viên thường sẽ chạy chương trình với các assertion được bật. Khi sự bất thành assertion xảy ra, lập trình viên sẽ ngay lập tức được báo tin về vấn đề. Nhiều bản thực hiện assertion cũng sẽ ngưng thực thi chương trình: điều này hữu ích, vì nếu chương trình tiếp tục chạy sau khi xảy ra sự vi phạm assertion thì nó có thể làm hỏng trạng thái của mình và làm cho nguyên nhân của vấn đề khó xác định hơn. Bằng thông tin được assertion cung cấp (chẳng hạn như vị trí của chỗ bị bất thành và có thể là cả stacktrace, hoặc thậm chí là trạng thái chương trình đầy đủ nếu môi trường đấy hỗ trợ core dump hoặc nếu chương trình đấy đang chạy trong debugger), lập trình viên thường có thể khắc phục vấn đề. Do đó, assertion là một công cụ rất mạnh trong việc debug.

Assertion trong môi trường sản xuất

Khi chương trình được triển khai vào sản xuất, assertion thường được tắt để tránh bất kỳ tác dụng phụ nào mà nó có thể mang. Trong một số trường hợp, assertion hoàn toàn vắng mặt trong code triển khai, chẳng hạn như trong assertion của C/C++ thông qua macro. Trong các trường hợp khác như Java chẳng hạn, assertion lại hiện diện trong code triển khai và có thể được bật lại bằng cờ tham số để debug.[2]

Assertion cũng có thể được sử dụng để hứa hẹn với trình biên dịch rằng một 'điều kiện biên' đã cho nào đó không thực sự có thể tới được, do đó cho phép trình biên dịch tiến hành các tối ưu hóa nhất định mà mặt khác ra không thể nào làm được. Trong trường hợp này, việc vô hiệu hóa assertion thực sự có thể làm giảm hiệu suất chương trình.

Assertion tĩnh

Assertion mà được kiểm tra tại thời điểm biên dịch thì được gọi là assertion tĩnh.

Assertion tĩnh có tính hữu ích trong template metaprogramming của compile-time nói riêng, nhưng cũng có thể được dùng trong các ngôn ngữ mức thấp như C bằng cách đưa code bất hợp lệ vào nếu (và chỉ nếu) assertion chạy không được. C11C++11 có hỗ trợ assertion tĩnh một cách trực tiếp thông qua static_assert. Trong các phiên bản C trước đó, assertion tĩnh có thể được thực hiện ví dụ như sau:

#define SASSERT(pred) switch(0){case 0:case pred:;}SASSERT( BOOLEAN CONDITION );

Nếu phần (BOOLEAN CONDITION) tính giá trị ra false thì đoạn mã trên sẽ không biên dịch được bởi vì trình biên dịch sẽ không cho phép hai nhãn case có cùng một hằng số. Biểu thức boolean phải là một giá trị hằng số trong compile-time, ví dụ: (sizeof(int)==4) sẽ là một biểu thức hợp lệ trong ngữ cảnh đó. Kết cấu này không dùng được ở tầm vực tập tin (tức là không ở bên trong hàm), và vì vậy nó phải được bọc bên trong hàm.

Một cách phổ biến khác[3] để thực hiện assertion trong C là:

static char const static_assertion[ (BOOLEAN CONDITION)                  ? 1 : -1                 ] = {'!'};

Nếu phần (BOOLEAN CONDITION) tính giá trị ra false thì đoạn mã trên sẽ không biên dịch được vì mảng không thể có độ dài âm. Nếu trong thực tế, trình biên dịch cho phép độ dài âm thì phần byte khởi tạo (phần '!') vẫn sẽ khiến ngay cả những trình biên dịch khoan dung nhất cũng phải phàn nàn. Biểu thức boolean phải là một giá trị hằng số trong compile-time, ví dụ: (sizeof(int) == 4) sẽ là một biểu thức hợp lệ trong ngữ cảnh đó.

D cung cấp assertion tĩnh bằng static assert.[4]

Tài liệu tham khảo

WikiPedia: Assertion (phát triển phần mềm) http://www.jaggersoft.com/pubs/CVu11_3.html http://docs.oracle.com/javase/8/docs/technotes/gui... http://docs.oracle.com/javase/8/docs/technotes/gui... http://sunnyday.mit.edu/16.355/Hoare-CACM-69.pdf http://queue.acm.org/detail.cfm?id=2220317 http://dlang.org/version.html#StaticAssert http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arn... http://discovery.ucl.ac.uk/4991/1/4991.pdf https://library.ias.edu/files/pdfs/ecp/planningcod... https://web.archive.org/web/2021*/http://lambda-th...